﻿namespace SsisLdapSource.DirectoryServicesProtocolsExtensions
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.DirectoryServices.Protocols;

    public class SearchResultParser
    {
        private readonly LdapConnection ldapConnection;

        public SearchResultParser(LdapConnection ldapConnection)
        {
            this.ldapConnection = ldapConnection;
        }

        public object GetSingleValuedAttributeData(
             LdapAttributeMetaMap ldapToDotNetMetaMap,
             DirectoryAttribute attribute)
        {
            if (ldapToDotNetMetaMap.SemanticValueConversionFunction == null)
            {
                return attribute.GetValues(ldapToDotNetMetaMap.RawDotNetTypeEquivalent)[0];
            }

            return
                ldapToDotNetMetaMap.SemanticValueConversionFunction(
                    attribute.GetValues(ldapToDotNetMetaMap.RawDotNetTypeEquivalent)[0]);
        }

        public IList GetMultiValuedAttributeData(
            LdapAttributeMetaMap ldapToDotNetMetaMap,
            DirectoryAttribute attribute,
            string directoryEntryDN)
        {
            var attributeValuesList = MakeGenericIListFromMultivalueAttribute(ldapToDotNetMetaMap);

            if (ldapToDotNetMetaMap.ExpansionMayBeRequired && attribute.GetValues(ldapToDotNetMetaMap.RawDotNetTypeEquivalent).Length == 0)
            {
                PerformExpandedEnumeration(
                    ldapToDotNetMetaMap,
                    directoryEntryDN,
                    attributeValuesList);
            }
            else
            {
                PerformEnumeration(
                    ldapToDotNetMetaMap,
                    attribute,
                    attributeValuesList);
            }

            return attributeValuesList;
        }

        private void PerformEnumeration(
            LdapAttributeMetaMap ldapToDotNetMetaMap, DirectoryAttribute attribute, IList attributeValuesList)
        {
            foreach (var value in attribute.GetValues(ldapToDotNetMetaMap.RawDotNetTypeEquivalent))
            {
                attributeValuesList.Add(
                    ldapToDotNetMetaMap.SemanticValueConversionFunction != null
                        ? ldapToDotNetMetaMap.SemanticValueConversionFunction(value)
                        : value);
            }
        }

        private void PerformExpandedEnumeration(
            LdapAttributeMetaMap ldapToDotNetMetaMap,
            string directoryEntryDN,
            IList attributeValuesList)
        {
            var rangeRetrievalSearchRequest = new RangeRetrievalSearchRequest(
                directoryEntryDN,
                ldapToDotNetMetaMap.LdapDisplayName);

            foreach (var value in rangeRetrievalSearchRequest.Execute(ldapConnection))
            {
                attributeValuesList.Add(
                    ldapToDotNetMetaMap.SemanticValueConversionFunction != null
                        ? ldapToDotNetMetaMap.SemanticValueConversionFunction(value)
                        : value);
            }
        }

        private IList MakeGenericIListFromMultivalueAttribute(LdapAttributeMetaMap ldapAttributeMetaMap)
        {
            var resultListType =
                typeof(List<>).MakeGenericType(
                        ldapAttributeMetaMap.SemanticDotNetTypeEquivalent ??
                        ldapAttributeMetaMap.RawDotNetTypeEquivalent);
            return (IList)Activator.CreateInstance(resultListType);
        }

        // Attributes which may require expansion will have the following format "member;range=0-1499"
        // the range portion needs to be stripped-out in order to produce metaMap
        public static string StripExpansionDirectiveFromAttributeNameIfPresent(string attributeNameWithExpansionDirective)
        {
            if (!string.IsNullOrEmpty(attributeNameWithExpansionDirective) && attributeNameWithExpansionDirective.Contains(";range="))
            {
                return attributeNameWithExpansionDirective.Split(';')[0];
            }

            return attributeNameWithExpansionDirective;
        }
    }
}
